#ifndef ZEXCEPTION_H_
#define ZEXCEPTION_H_

#include "GlobalDefines.h"
#include <exception>
#include <string>

GLOBAL_NAMESPACE_BEGIN

class Exception : public std::exception
{
public:
    enum ExceptionCodes {
        ERR_INVALID_PARAMS,
        ERR_INVALID_STATE,
        ERR_CANNOT_WRITE_TO_FILE,
        ERR_FILE_NOT_FOUND,
        ERR_NOT_IMPLEMENTED
    };

public:
    Exception(int number, const std::string& description, const std::string& source);
    Exception(int number, const std::string& description, const std::string& source, const char* tpye, const char* file, long line);
    Exception(const Exception& rhs);
    ~Exception() throw() {}

    void operator=(const Exception& rhs);

    virtual const std::string& getFullDescription(void) const;

    virtual int getNumber(void) const throw();

    virtual const std::string& getSource() const        { return source; }
    virtual const std::string& getFile()   const        { return file; }
    virtual long               getLine()   const        { return line; }
    virtual const std::string& getDescription() const   { return description; }

    // override std::exception::what
    const char* what() const throw() { return getFullDescription().c_str(); }

protected:
    long line; 
    int number;
    std::string typeName;
    std::string description;
    std::string source;
    std::string file;
    mutable std::string fullDesc;

};

template <int num>
struct ExceptionCodeTpye
{
    enum {number = num};
};

class UnimplementedException : public Exception
{
public:
    UnimplementedException(int inNumber, const std::string& inDescription, const std::string& inSource,
        const char* inFile, long inLine)
        : Exception(inNumber, inDescription, inSource, "UnimplementedException", inFile, inLine) {}
};
    
class FileNotFoundException : public Exception
{
public:
    FileNotFoundException(int inNumber, const std::string& inDescription, const std::string& inSource,
        const char* inFile, long inLine)
        : Exception(inNumber, inDescription, inSource, "FileNotFoundException", inFile, inLine) {}
};

class InvalidInputException : public Exception
{
public:
    InvalidInputException(int inNumber, const std::string& inDescription, const std::string& inSource,
        const char* inFile, long inLine)
        : Exception(inNumber, inDescription, inSource, "InvalidInputException", inFile, inLine) {}
};

class InvalidParametersException : public Exception
{
public:
    InvalidParametersException(int inNumber, const std::string& inDescription, const std::string& inSource,
        const char* inFile, long inLine)
        : Exception(inNumber, inDescription, inSource, "InvalidParametersException", inFile, inLine) {}
};
  
class InvalidStateException : public Exception
{
public:
    InvalidStateException(int inNumber, const std::string& inDescription, const std::string& inSource,
        const char* inFile, long inLine)
        : Exception(inNumber, inDescription, inSource, "InvalidStateException", inFile, inLine) {}
};

class ExceptionFactory
{
private:
    ExceptionFactory(){}
public:
    static UnimplementedException create(
        ExceptionCodeTpye<Exception::ERR_NOT_IMPLEMENTED> code,
        const std::string& desc,
        const std::string& src, const char* file, long line
        )
    {
        return UnimplementedException(code.number, desc, src, file, line);
    }

    static FileNotFoundException create(
        ExceptionCodeTpye<Exception::ERR_FILE_NOT_FOUND> code,
        const std::string& desc,
        const std::string& src, const char* file, long line
        )
    {
        return FileNotFoundException(code.number, desc, src, file, line);
    }

    static InvalidStateException create(
        ExceptionCodeTpye<Exception::ERR_INVALID_STATE> code,
        const std::string& desc,
        const std::string& src, const char* file, long line
        )
    {
        return InvalidStateException(code.number, desc, src, file, line);
    }

    static InvalidParametersException create(
        ExceptionCodeTpye<Exception::ERR_INVALID_PARAMS> code,
        const std::string& desc,
        const std::string& src, const char* file, long line
        )
    {
        return InvalidParametersException(code.number, desc, src, file, line);
    }
};

#ifndef ZFW_EXCEPTION
#define ZFW_EXCEPTION(num, desc, src) throw ZFramework::ExceptionFactory::create(\
            ZFramework::ExceptionCodeTpye<num>(), desc, src, __FILE__, __LINE__);
#endif//ZFW_EXCEPTION

GLOBAL_NAMESPACE_END

#endif//ZEXCEPTION_H_
